home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 9 / CDACTUAL9.iso / share / Dos / VARIOS / pascal / SOUND.SWG / 0001_CD Audio Player.pas next >
Encoding:
Pascal/Delphi Source File  |  1996-02-21  |  19.5 KB  |  634 lines

  1. UNIT CDAUDIO;
  2. { by Eric Miller, Jan. 15, 1996 }
  3. { public domain }
  4. INTERFACE
  5.  
  6. Uses Objects;
  7.  
  8. CONST
  9.         cdPlaying = 1;     { drive is playing audio }
  10.         cdDisc = 2;        { disc is in drive }
  11.         cdDoor = 4;        { drive door is closed }
  12. TYPE
  13.         TRedBook = RECORD
  14.                 Frame, Second, Minute, Unused: Byte;
  15.         END;
  16.         TTrack = RECORD
  17.                 Start, Finish: TRedBook;
  18.         END;
  19.         TDeviceHeader = RECORD
  20.                 NextDriver: Pointer;
  21.                 Attribute, StratEntry, IntEntry: Word;
  22.                 Name: array[0..7] of Char;
  23.                 Res: Word;
  24.                 Letter, Units: Byte;
  25.         END;
  26.         TDevice = RECORD
  27.                 Subunit: Byte;
  28.                 Header: Pointer;
  29.         END;
  30.         TRequestHeader = RECORD
  31.                 Length,        Subunit, Command: Byte;
  32.                 Status: Word;
  33.                 Res: array[0..7] of Byte;
  34.         END;
  35.         TPlayRequest = RECORD
  36.                 Header: TRequestHeader;
  37.                 Addressing: Byte;
  38.                 Start, Length: LongInt;
  39.         END;
  40.         TIOCTLRequest = RECORD
  41.                 Header: TRequestHeader;
  42.                 Media: Byte;
  43.                 Address: Pointer;
  44.                 Length,        Start: Word;
  45.                 Res: Pointer;
  46.         END;
  47.         TQChannel = RECORD
  48.                 Command, CTRL_ADR, Track,        Point: Byte;
  49.                 Min, Sec, Frame, Zero, AMin, ASec, AFrame: Byte;
  50.         END;
  51.         TDeviceStatus = RECORD
  52.                 Command: Byte;
  53.                 Status: LongInt;
  54.         END;
  55.         TCommand = RECORD
  56.                 Command: Byte;
  57.         END;
  58.         TDiskInfo = RECORD
  59.                 Command: Byte;
  60.                 LowTrack, HighTrack: Byte;
  61.                 LeadOut: TRedBook;
  62.         END;
  63.         TTrackInfo = RECORD
  64.                 Command, Track: Byte;
  65.                 Start: TRedBook;
  66.                 Control: Byte;
  67.         END;
  68. TYPE
  69.         TCD = OBJECT
  70.                 Drive,                 { CD-ROM drive }
  71.                 Letter: Byte;          { drive letter }
  72.                 Header: ^TDeviceHeader;
  73.                 Subunit: Byte;
  74.                 Name: String[8]; { name of driver }
  75.                 Handle: Word;
  76.                 LowTrack, HighTrack, CurTrack: Byte;
  77.                 cMin, cSec, cFrame,                                                { track time }
  78.                 tMin, tSec, tFrame,                                                { track length }
  79.                 dMin, dSec, dFrame: Byte;           { disc time }
  80.                 Tracks: array[1..64] of TTrack;        { track info }
  81.                 Status: Word;                                                                        { drive status }
  82.                 PROCEDURE Eject;
  83.                 PROCEDURE Retract;
  84.                 FUNCTION State(AState: Word): Boolean; { test Status field }
  85.                 PROCEDURE GetStatus;                                        { set Status field }
  86.                 PROCEDURE Error(Code: Word);
  87.                 PROCEDURE Stop;
  88.                 FUNCTION RedToHSG(Redbook: TRedbook): LongInt;
  89.                 PROCEDURE HSGToRed(HSG: Longint; VAR Redbook: TRedbook);
  90.                 FUNCTION Time(M, S, F: Byte): String;        { Redbook > 'mm:ss:ff' }
  91.                 FUNCTION Drives: Byte;                                        { # of CD-ROM drives }
  92.                 FUNCTION Version: Word;                                        { MSCDEX version }
  93.                 FUNCTION MediaChanged: Byte;                { disc ready/different/etc }
  94.                 PROCEDURE PlayTrack(Track: Byte);
  95.                 PROCEDURE GetPosition;                                        { get play position }
  96.                 PROCEDURE GetTracks;                                                { get track info }
  97.                 PROCEDURE GetHandle;
  98.                 PROCEDURE GetSubUnit;
  99.                 PROCEDURE GetDriveLetter;
  100.                 PROCEDURE GetDriverName;
  101.                 FUNCTION Init: Boolean;
  102.                 PROCEDURE Done;
  103.         END;
  104.  
  105. IMPLEMENTATION
  106.  
  107. PROCEDURE TCD.Done;
  108. BEGIN
  109.         Drive := 0;        Header := NIL;
  110.         Name := '';        LowTrack := 0;
  111.         HighTrack := 0;        Status := 0;
  112. END;
  113.  
  114. FUNCTION TCD.State(AState: Word): Boolean;
  115. BEGIN
  116.         State := Status AND AState = AState;
  117. END;
  118.  
  119. PROCEDURE TCD.Error(Code: Word);
  120. BEGIN
  121.         IF Status AND $8000 > 0 THEN
  122.                 BEGIN
  123.                         Write('ERROR - (', Status AND $F, ') ');
  124.                         CASE (Status AND $F) of
  125.                                 0: Writeln('Write-protect violation');
  126.                                 1: Writeln('Unknown unit');
  127.                                 2: Writeln('Drive not ready');
  128.                                 3: Writeln('Unknown command');
  129.                                 4: Writeln('CRC error');
  130.                                 5: Writeln('Bad drive request structure length');
  131.                                 6: Writeln('Seek error');
  132.                                 7: Writeln('Unknown media');
  133.                                 8: Writeln('Sector not found');
  134.                                 9: Writeln('Printer out of paper');
  135.                          10: Writeln('Write fault');
  136.                          11: Writeln('Read fault');
  137.                          12: Writeln('General failure');
  138.                          13..14: Writeln('Reserved error');
  139.                          15: Writeln('Invalid disk change');
  140.                         END;
  141.                 END;
  142. END;
  143.  
  144. FUNCTION TCD.Init: Boolean;
  145. BEGIN
  146.         IF Drives > 0 THEN
  147.                 BEGIN
  148.                         Init := True;
  149.                         Drive := 1;
  150.                         GetDriveLetter;
  151.                         GetSubunit;
  152.                         GetDriverName;
  153.                         GetHandle;
  154.                         MediaChanged;
  155.                         GetPosition;
  156.                         GetStatus;
  157.                 END
  158.         ELSE
  159.                 Init := False;
  160. END;
  161.  
  162. PROCEDURE TCD.Eject;
  163. VAR
  164.         E: TCommand;
  165.         IOCTLO: TIOCTLRequest;
  166.         Offs, Segm: Word;
  167.         ALetter: Byte;
  168. BEGIN
  169.         ALetter := Letter;
  170.         Segm := Seg(IOCTLO);
  171.         Offs := Ofs(IOCTLO);
  172.         WITH IOCTLO DO
  173.                 BEGIN
  174.                         Media := 0;
  175.                         Address := @E;
  176.                         Length := SizeOf(E);
  177.                         Start := 0;
  178.                         Res := NIL;
  179.                         WITH Header DO
  180.                                 BEGIN
  181.                                         Length := 26;        Command := 12;
  182.                                 END;
  183.                         Header.Subunit := Subunit;
  184.                 END;
  185.         E.Command := $0;
  186.         ASM
  187.                 mov ax,Segm
  188.                 mov es,ax
  189.                 mov bx,Offs
  190.                 mov cl,ALetter
  191.                 mov ax,1510h
  192.                 int 2fh
  193.         END;
  194. END;
  195.  
  196. PROCEDURE TCD.Retract;
  197. VAR
  198.         R: TCommand;
  199.         IOCTLO: TIOCTLRequest;
  200.         Offs, Segm: Word;
  201.         ALetter: Byte;
  202. BEGIN
  203.         ALetter := Letter;
  204.         Segm := Seg(IOCTLO);
  205.         Offs := Ofs(IOCTLO);
  206.         WITH IOCTLO DO
  207.                 BEGIN
  208.                         Media := 0;
  209.                         Address := @R;
  210.                         Length := SizeOf(R);
  211.                         Start := 0;
  212.                         Res := NIL;
  213.                         WITH Header DO
  214.                                 BEGIN
  215.                                         Length := 26;        Command := 12;
  216.                                 END;
  217.                         Header.Subunit := Subunit;
  218.                 END;
  219.         R.Command := $5;
  220.         ASM
  221.                 mov ax,Segm
  222.                 mov es,ax
  223.                 mov bx,Offs
  224.                 mov cl,ALetter
  225.                 mov ax,1510h
  226.                 int 2fh
  227.         END;
  228. END;
  229.  
  230.  
  231. PROCEDURE TCD.Stop;
  232. VAR
  233.  Request: TRequestHeader;
  234.  Segm, Offs: Word;
  235.  ALetter: Byte;
  236. BEGIN
  237.         ALetter := Letter;
  238.         Segm := Seg(Request);
  239.         Offs := Ofs(Request);
  240.         WITH Request DO
  241.                 BEGIN
  242.                         Length := 5;
  243.                         Command := $85;
  244.                 END;
  245.         Request.Subunit := Subunit;
  246.         ASM
  247.                 push ds
  248.                 mov  ax,Segm
  249.                 mov  es,ax
  250.                 mov  bx,Offs
  251.                 xor  cx,cx
  252.                 mov  cl,ALetter
  253.                 mov  ax,1510h
  254.                 int  2fh
  255.                 pop  ds
  256.         END
  257. END;
  258.  
  259. PROCEDURE TCD.GetStatus;
  260. VAR
  261.         ALetter: Byte;
  262.         DeviceStatus: TDeviceStatus;
  263.         IOCTLI: TIOCTLRequest;
  264.         Segm, Offs: Word;
  265. BEGIN
  266.         ALetter := Letter;
  267.         Segm := Seg(IOCTLI);
  268.         Offs := Ofs(IOCTLI);
  269.         WITH IOCTLI DO
  270.                 BEGIN
  271.                         Media := 0;
  272.                         Address := @DeviceStatus;
  273.                         Length := SizeOf(DeviceStatus);
  274.                         Start := 0;
  275.                         Res := NIL;
  276.                         WITH Header DO
  277.                                 BEGIN
  278.                                         Length := 26;        Command := 3;
  279.                                 END;
  280.                         Header.Subunit := Subunit;
  281.                 END;
  282.         DeviceStatus.Command := $6;
  283.  
  284.   ASM
  285.                 mov ax,Segm
  286.                 mov es,ax
  287.                 mov bx,Offs
  288.                 mov cl,ALetter
  289.                 mov ax,1510h
  290.                 int 2fh
  291.         END;
  292.         Status := 0;
  293.         IF IOCTLI.Header.Status AND $200 > 0 THEN
  294.                 Inc(Status, cdPlaying);
  295.         IF NOT (DeviceStatus.Status AND $800 > 0) THEN
  296.                 Inc(Status, cdDisc);
  297.         IF DeviceStatus.Status AND 1 = 0 THEN
  298.                 Inc(Status, cdDoor);
  299. END;
  300.  
  301. FUNCTION TCD.Time(M, S, F: Byte): String;
  302. VAR
  303.         St: String;
  304.         T: LongInt;
  305. BEGIN
  306.         T := (longint(M MOD 100) * 10000) + ((S MOD 60) * 100) + (F MOD 75);
  307.         Str(T:6, St);
  308.         IF T > 99 THEN
  309.                 BEGIN
  310.                         Insert(':', St, 5);
  311.                         IF T > 9999 THEN
  312.                                 Insert(':', St, 3)
  313.                         ELSE
  314.                                 St := Concat(' ', St);
  315.                 END
  316.         ELSE
  317.                 St := Concat('  ', St);
  318.         Time := St;
  319. END;
  320.  
  321. PROCEDURE TCD.GetPosition;
  322. VAR
  323.         Segm, Offs: Word;
  324.         QChannel: TQChannel;
  325.         IOCTLI: TIOCTLRequest;
  326.         T: Longint;
  327.         S: String;
  328.         ALetter: Byte;
  329. BEGIN
  330.         ALetter := Letter;
  331.         Segm := Seg(IOCTLI);
  332.         Offs := Ofs(IOCTLI);
  333.         WITH IOCTLI DO
  334.                 BEGIN
  335.                         Media := 0;
  336.                         Address := @QChannel;
  337.                         Length := SizeOf(QChannel);
  338.                         Start := 0;
  339.                         Res := NIL;
  340.                         WITH Header DO
  341.                                 BEGIN
  342.                                         Length := 26;        Command := 3;
  343.                                 END;
  344.                         Header.Subunit := Subunit;
  345.                 END;
  346.         QChannel.Command := 12;
  347.         ASM
  348.                 mov ax,Segm
  349.                 mov es,ax
  350.                 mov bx,Offs
  351.                 xor cx,cx
  352.                 mov cl,ALetter
  353.                 mov ax,1510h
  354.                 int 2fh
  355.         END;
  356.         WITH QChannel DO
  357.                 BEGIN
  358.                         cMin := Min; cSec := Sec;        cFrame := Frame;
  359.                         dMin := AMin;        dSec := ASec;        dFrame := AFrame;
  360.                 END;
  361. END;
  362.  
  363. FUNCTION TCD.RedToHSG(Redbook: TRedBook): LongInt;
  364. BEGIN
  365.         WITH Redbook DO
  366.                 RedToHSG := (longint(Minute) * 4500) + (Second * 75) + Frame - 150;
  367. END;
  368.  
  369. PROCEDURE TCD.HSGToRed(HSG: Longint; VAR Redbook: TRedbook);
  370. BEGIN
  371.         Inc(HSG, 150);
  372.         WITH RedBook DO
  373.                 BEGIN
  374.                         Minute := HSG DIV 4500;
  375.                         Second := HSG DIV 75 MOD 60;
  376.                         Frame := HSG MOD 75;
  377.                 END;
  378. END;
  379.  
  380. PROCEDURE TCD.PlayTrack(Track: Byte);
  381. VAR
  382.  Segm, Offs: Word;
  383.  ADrive: Byte;
  384.  PlayRequest: TPlayRequest;
  385.  Red_Length: TRedbook;
  386.         StartHSG, EndHSG, HSG_Length: Longint;
  387. BEGIN
  388.         CurTrack := Track;
  389.         StartHSG := RedToHSG(Tracks[Track].Start);
  390.         EndHSG := RedToHSG(Tracks[Track].Finish);
  391.         HSG_Length := EndHSG - StartHSG;
  392.         HSGToRed(HSG_Length, Red_Length);
  393.         WITH Red_Length DO
  394.                 BEGIN
  395.                         tMin := Minute; tSec := Second; tFrame := Frame;
  396.                 END;
  397.         Segm := Seg(PlayRequest);
  398.         Offs := Ofs(PlayRequest);
  399.         ADrive := Letter;
  400.         WITH PlayRequest DO
  401.                 BEGIN
  402.                         Header.Length := SizeOf(PlayRequest);
  403.                         Header.Subunit := SubUnit;
  404.                         Header.Command := $84;
  405.                         Addressing := 0;
  406.                         Start := RedToHSG(Tracks[Track].Start);
  407.                         Length := RedToHSG(Tracks[Track].Finish) - RedToHSG(Tracks[Track].Start);
  408.                 END;
  409.         ASM
  410.                 push ds
  411.                 mov  ax,Segm
  412.                 mov  es,ax
  413.                 mov  bx,Offs
  414.                 xor  cx,cx
  415.                 mov  cl,ADrive
  416.                 mov  ax,1510h
  417.                 int  2fh
  418.                 pop  ds
  419.         END;
  420. END;
  421.  
  422. PROCEDURE TCD.GetTracks;
  423. VAR
  424.         DiskInfo: TDiskInfo;
  425.         TrackInfo: TTrackInfo;
  426.         Segm, Offs: Word;
  427.         Z: Byte;
  428.         AHandle: Word;
  429. BEGIN
  430.         AHandle := Handle;
  431.         Segm := Seg(DiskInfo);
  432.         Offs := Ofs(DiskInfo);
  433.         DiskInfo.Command := $A;
  434.         ASM
  435.                 push ds
  436.                 mov  ax,Segm
  437.                 mov  ds,ax
  438.                 mov  dx,Offs
  439.                 mov  bx,AHandle
  440.                 mov  cx,7
  441.                 mov  ax,4402h
  442.                 int  21h
  443.                 pop  ds
  444.         END;
  445.  
  446.         HighTrack := DiskInfo.HighTrack;
  447.         LowTrack := DiskInfo.LowTrack;
  448.  
  449.         Segm := Seg(TrackInfo);
  450.         Offs := Ofs(TrackInfo);
  451.         TrackInfo.Command := $B;
  452.  
  453.         FOR Z := LowTrack TO HighTrack DO
  454.                 BEGIN
  455.                         TrackInfo.Track := Z;
  456.                         ASM
  457.                                 push ds
  458.                                 mov  ax,Segm
  459.                                 mov  ds,ax
  460.                                 mov  dx,Offs
  461.                                 mov  bx,AHandle
  462.                                 mov  cx,7
  463.                                 mov  ax,4402h
  464.                                 int  21h
  465.                                 pop  ds
  466.                         END;
  467.                         Tracks[Z].Start := TrackInfo.Start;
  468.                 END;
  469.  
  470.         FOR Z := LowTrack + 1 TO HighTrack DO
  471.                 BEGIN
  472.                         TrackInfo.Track := Z;
  473.                         Tracks[Z - 1].Finish := Tracks[Z].Start;
  474.                 END;
  475.         Tracks[HighTrack].Finish := DiskInfo.LeadOut;
  476. END;
  477.  
  478. FUNCTION TCD.MediaChanged: Byte;
  479. VAR
  480.         AHandle, Segm, Offs: Word;
  481.         Buffer: array[0..127] of Byte;
  482. BEGIN
  483.         Segm := Seg(Buffer);
  484.         Offs := Ofs(Buffer);
  485.         AHandle := Handle;
  486.         ASM
  487.                 push ds
  488.                 mov  ax,Segm
  489.                 mov  ds,ax
  490.                 mov  dx,Offs
  491.                 mov  bx,dx
  492.                 mov  al,9h
  493.                 mov  ds:[bx],al
  494.                 mov  bx,AHandle
  495.                 mov  cx,2
  496.                 mov  ax,4402h
  497.                 int  21h
  498.                 pop  ds
  499.         END;
  500.         MediaChanged := Buffer[1];
  501. END;
  502.  
  503. PROCEDURE TCD.GetHandle;
  504. VAR
  505.         Result, Segm, Offs: Word;
  506. BEGIN
  507.         Segm := Seg(Name);
  508.         Offs := Succ(Ofs(Name));
  509.         ASM
  510.                 push ds
  511.                 mov  ax,Segm
  512.                 mov  ds,ax
  513.                 mov  dx,Offs
  514.                 mov  ah,3dh
  515.                 mov  al,2h
  516.                 int  21h
  517.                 jc   @1
  518.                 mov         Result,ax
  519.                 jmp @2
  520.                 @1:
  521.                 mov Result,0h
  522.                 @2:
  523.                 pop ds
  524.         END;
  525.         Handle := Result;
  526. END;
  527.  
  528. PROCEDURE TCD.GetDriverName;
  529. BEGIN
  530.         Name := '';
  531.         Move(Header^.Name, Name[1], 8);
  532.         REPEAT
  533.                 Inc(Name[0]);
  534.         UNTIL (Length(Name) = 8) OR (Name[Length(Name)] = #32);
  535. END;
  536.  
  537. PROCEDURE TCD.GetDriveLetter;
  538. VAR
  539.         DriveLetterList: array[1..26] of Byte;
  540.         Segm, Offs: Word;
  541. BEGIN
  542.         Segm := Seg(DriveLetterList);
  543.         Offs := Ofs(DriveLetterList);
  544.  
  545.   ASM
  546.                 mov ax,Segm
  547.                 mov es,ax
  548.                 mov bx,Offs
  549.                 mov ax,150Dh
  550.                 int 2fh
  551.         END;
  552.         Letter := DriveLetterList[Drive];
  553. END;
  554.  
  555.  
  556. FUNCTION TCD.Version: Word; assembler;
  557. ASM
  558.         mov  ax,150ch
  559.         int  2fh
  560.         mov  ax,bx
  561. END;
  562.  
  563.  
  564. PROCEDURE TCD.GetSubunit;
  565. VAR
  566.         DeviceList: array[1..26] of TDevice;
  567.         Segm, Offs: Word;
  568. BEGIN
  569.         Segm := Seg(DeviceList);
  570.         Offs := Ofs(DeviceList);
  571.         ASM
  572.                 mov ax,Segm
  573.                 mov es,ax
  574.                 mov bx,Offs
  575.                 mov ax,1501h
  576.                 int 2fh
  577.         END;
  578.         Subunit := DeviceList[Drive].Subunit;
  579.         Header := DeviceList[Drive].Header;
  580. END;
  581.  
  582. FUNCTION TCD.Drives: Byte; assembler;
  583. ASM
  584.         mov ax,1500h
  585.         mov bx,0
  586.         int 2fh
  587.         mov al,bl
  588. END;
  589.  
  590. END.
  591.  
  592. { ----------------------   DEMO PROGRAM ------------------ }
  593.  
  594. PROGRAM CDPLAY;
  595. { Test program for CDAUDIO unit.        
  596.         Plays audio tracks sequentially until keypressed.
  597. }
  598. Uses CRT, CDAUDIO;
  599.  
  600. VAR
  601.         CD: TCD;
  602.         Track: Byte;
  603.         Ticker: Longint ABSOLUTE $40:$6c;
  604.         Tick: Longint;
  605. BEGIN
  606.         IF CD.Init THEN
  607.                 WITH CD DO
  608.                         BEGIN
  609.                                 GetTracks;
  610.                                 Writeln(CD.HighTrack, ' tracks');
  611.                                 Track := LowTrack;
  612.                                 WHILE (Track <= HighTrack) AND NOT Keypressed DO
  613.                                         BEGIN
  614.                                                 PlayTrack(Track);
  615.                                                 Write('TRACK ', Track, ' LENGTH ', Time(tMin, tSec, tFrame), ' TIME ');
  616.                                                 REPEAT
  617.                                                         Tick := Ticker;
  618.                                                         REPEAT UNTIL Ticker <> Tick;
  619.                                                         GetPosition;
  620.                                                         Write(Time(cMin, cSec, cFrame));
  621.                                                         GetStatus;
  622.                                                         Write(#8#8#8#8#8#8#8#8);
  623.                                                 UNTIL Keypressed OR NOT State(cdPlaying);
  624.                                                 Writeln;
  625.                                                 Inc(Track);
  626.                                         END;
  627.                         IF State(cdPlaying) THEN
  628.                                 Stop;
  629.                         Done;
  630.                 END
  631.         ELSE
  632.                 Writeln('No CD-ROM drive?!');
  633. END.
  634.